package grame;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;

class Panel extends JPanel
{
	private static final long serialVersionUID = 1L;
	private int rows;
	private int columns;
	private Color defaultColor;
	private Color groutingColor;
	private boolean alwaysDrawGrouting;
	private boolean autopaint = true;
	private Color[][] grid;
	private BufferedImage OSI;
	private boolean needsRedraw;
	private String[][] text;
	private double fontSize;
	private boolean squares3D;
	private int squareSpace;
	private Color fontColor;

	public Panel()
	{
		this(42, 42, 16, 16);
	}

	public Panel(int rows, int columns)
	{
		this(rows, columns, 16, 16);
	}

	public Panel(int rows, int columns, int preferredBlockWidth, int preferredBlockHeight)
	{
		this(rows, columns, preferredBlockWidth, preferredBlockHeight, null, 2);
	}

	public Panel(int rows, int columns, int preferredBlockWidth, int preferredBlockHeight, Color borderColor, int borderWidth)
	{
		this.fontSize = 1.0D;
		this.fontColor = Color.BLACK;
		this.rows = rows;
		this.columns = columns;
		this.grid = new Color[rows][columns];
		this.text = new String[rows][columns];
		for (int i = 0; i < rows; i++)
			for (int j = 0; j < columns; j++)
				this.text[i][j] = new String();
		this.defaultColor = Color.black;
		this.groutingColor = Color.black;
		this.alwaysDrawGrouting = false;
		this.squares3D = false;
		this.squareSpace = 1;
		setBackground(this.defaultColor);
		setOpaque(true);
		setDoubleBuffered(false);

		if ((preferredBlockWidth > 0) && (preferredBlockHeight > 0))
		{
			setPreferredSize(new Dimension(preferredBlockWidth * columns, preferredBlockHeight * rows));
		}
	}

	public void setDefaultColor(Color c)
	{
		if (c == null)
			c = Color.black;
		if (!c.equals(this.defaultColor)) {
			this.defaultColor = c;
			setBackground(c);
			forceRedraw();
		}
	}

	public Color getDefaultColor()
	{
		return this.defaultColor;
	}

	public void setGroutingColor(Color c)
	{
		if ((c == null) || (!c.equals(this.groutingColor))) {
			this.groutingColor = c;
			forceRedraw();
		}
	}

	public Color getGroutingColor(Color c)
	{
		return this.groutingColor;
	}

	public void setAlwaysDrawGrouting(boolean always)
	{
		if (this.alwaysDrawGrouting != always) {
			this.alwaysDrawGrouting = always;
			forceRedraw();
		}
	}

	public boolean getAlwaysDrawGrouting()
	{
		return this.alwaysDrawGrouting;
	}

	public void setGridSize(int rows, int columns, boolean preserveData)
	{
		if ((rows > 0) && (columns > 0)) {
			Color[][] newGrid = new Color[rows][columns];
			if (preserveData) {
				int rowMax = Math.min(rows, this.rows);
				int colMax = Math.min(columns, this.columns);
				for (int r = 0; r < rowMax; r++)
					for (int c = 0; c < colMax; c++)
						newGrid[r][c] = this.grid[r][c];
			}
			this.grid = newGrid;
			this.rows = rows;
			this.columns = columns;
			forceRedraw();
		}
	}

	public int getRowCount()
	{
		return this.rows;
	}

	public int getColumnCount()
	{
		return this.columns;
	}
	public void fill(Color c)
	{
		for (int i = 0; i < this.rows; i++)
			for (int j = 0; j < this.columns; j++)
				this.grid[i][j] = c;
		forceRedraw();
	}
	public void clear()
	{
		fill(null);
	}
	public boolean getAutopaint()
	{
		return this.autopaint;
	}
	public final void forceRedraw()
	{
		this.needsRedraw = true;
		repaint();
	}
	public BufferedImage getImage()
	{
		return this.OSI;
	}
	public void setSquareText(Coordinates c, String text)
	{
		this.text[c.getY()][c.getX()] = text;
		drawSquare(c.getY(), c.getX());
	}
	public String getSquareText(int row, int column)
	{
		return this.text[row][column];
	}
	public String getSquareText(Coordinates c)
	{
		return this.text[c.getY()][c.getX()];
	}
	public void setFontSize(double fs)
	{
		this.fontSize = fs;

		forceRedraw();
	}

	public void setFlatSquares(boolean status)
	{
		this.squares3D = status;
		forceRedraw();
	}

	public void setTextFontColor(Color fontColor)
	{
		this.fontColor = fontColor;
		forceRedraw();
	}

	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		if ((this.OSI == null) || (this.OSI.getWidth() != getWidth()) || (this.OSI.getHeight() != getHeight())) {
			this.OSI = new BufferedImage(getWidth(), getHeight(), 1);
			this.needsRedraw = true;
		}
		if (this.needsRedraw) {
			Graphics OSG = this.OSI.getGraphics();
			for (int r = 0; r < this.rows; r++)
				for (int c = 0; c < this.columns; c++)
					drawSquare(OSG, r, c, false);
			OSG.dispose();
			this.needsRedraw = false;
		}
		g.drawImage(this.OSI, 0, 0, null);
	}

	private void drawSquare(Graphics gr, int row, int col, boolean callRepaint)
	{
		Graphics2D g = (Graphics2D)gr;

		if ((callRepaint) && (!this.autopaint))
			return;
		Insets insets = getInsets();
		insets.right = 1;
		insets.bottom = 1;
		double rowHeight = (getHeight() - insets.left - insets.right) / this.rows;
		double colWidth = (getWidth() - insets.top - insets.bottom) / this.columns;
		int xOffset = insets.left;
		int yOffset = insets.top;
		int y = yOffset + (int)Math.round(rowHeight * row);
		int h = Math.max(1, (int)Math.round(rowHeight * (row + 1)) + yOffset - y);
		int x = xOffset + (int)Math.round(colWidth * col);
		int w = Math.max(1, (int)Math.round(colWidth * (col + 1)) + xOffset - x);

		g.setColor(this.grid[row][col]);

		if (!this.squares3D)
			g.fillRect(x + this.squareSpace, y + this.squareSpace, w - this.squareSpace, h - this.squareSpace);
		else {
			g.fill3DRect(x + this.squareSpace, y + this.squareSpace, w - this.squareSpace, h - this.squareSpace, true);
		}
		g.setColor(this.groutingColor);
		if (this.squareSpace > 0) {
			g.drawRect(x, y, w, h);
		}

		String str = this.text[row][col];
		if (str == null)
			str = "";
		int fontSize = (int)(w / 2.7D * this.fontSize);

		g.setFont(new Font("Arial", 0, fontSize));
		FontRenderContext frc = g.getFontRenderContext();
		Rectangle2D bounds = g.getFont().getStringBounds(str, frc);
		LineMetrics lm = g.getFont().getLineMetrics(str, frc);
		float baselineY = y - lm.getHeight() / 2.0F + lm.getAscent();
		g.setColor(this.fontColor);
		g.drawString(str, (float)(x + (w - bounds.getWidth()) / 2.0D), baselineY + h / 1.8F);

		if (callRepaint)
			repaint(x, y, w, h);
	}
	private void drawSquare(int row, int col)
	{
		if (this.OSI == null) {
			repaint();
		}
		else {
			Graphics g = this.OSI.getGraphics();
			drawSquare(g, row, col, true);
			g.dispose();
		}
	}
	void setColor(Coordinates c, Color color)
	{
		int row=c.getY(), col=c.getX();
		if ((row >= 0) && (row < this.rows) && (col >= 0) && (col < this.columns)) 
		{
			this.grid[row][col] = color;
			drawSquare(row, col);
		}
	}
	public void setColor(Coordinates c, int red, int green, int blue)
	{
		int row=c.getY(), col=c.getX();
		if ((row >= 0) && (row < this.rows) && (col >= 0) && (col < this.columns)) {
			red = red > 255 ? 255 : red < 0 ? 0 : red;
			green = green > 255 ? 255 : green < 0 ? 0 : green;
			blue = blue > 255 ? 255 : blue < 0 ? 0 : blue;
			this.grid[row][col] = new Color(red, green, blue);
			drawSquare(row, col);
		}
	}
	public Color getColor(Coordinates c)
	{
		int y=c.getY(), x=c.getX();
		if ((y >= 0) && (y < this.rows) && (x >= 0) && (x < this.columns)) {
			return this.grid[y][x];
		}
		return null;
	}
	void setSquare(Coordinates c, Color col, String st)
	{
		setSquareText(c, st);
		setColor(c, col);
	}
}